# -*- coding: utf-8 -*-
'''
# @Datetime: 2018/12/24
# @author: Zhang Yafei
'''
"""
正常情况下，当我们定义了一个class，创建了一个class的实例后，我们可以给该实例绑定任何属性和方法，这就是动态语言的灵活性
"""
import io
import sys
#改变标准输出的默认编码
sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')

from types import MethodType


class Obj(object):
    # __slots__ = ('name', 'age')

    def __init__(self):
        self.name = '张亚飞'


def set_score(self, score):
    self.score = score


@staticmethod
def static_func():
    print('static_func')


@classmethod
def class_func(cls):
    print('class_method')


"""
动态语言的灵活性
1.动态添加属性
"""
obj = Obj()
# 1.1 添加对象属性
print(obj.name)
obj.age = 23
print(obj.age)
obj2 = Obj()
# print(obj2.age)
"""
由以上代码可知，Obj类有两个属性：name和age。通过[对象名.属性名]给类对象obj动态添加了对象属性addr，而Obj的另一个类对象obj2却不能调用这个属性。
注：通过对象名添加的对象属性，只有这个对象能使用
"""
# 1.2 添加类属性
Obj.score = 100
print(obj.score)
print(obj2.score)
"""
由以上代码可知，通过[类名.属性名]给类Obj动态添加了类属性addr，Obj的类对象obj和obj2都能调用这个属性
注：通过类名添加的类属性，这个类的所有对象都能使用
"""
"""
2. 动态添加方法
类中有三种方法，实例方法，静态方法和类方法，三种方法的区别如下：
实例方法：需要绑定要一个对象上，第一个参数默认使用self，会把对象作为第一个参数传递进来
静态方法：使用装饰器@staticmethod进行定义，类和对象都可以调用，不需要默认参数
类方法：使用装饰器@classmethod进行定义，类和对象都可以调用，第一个参数默认使用cls，会把类作为第一个参数传递进来
"""
# 2.1 动态添加实例方法
obj.set_score = MethodType(set_score, obj)
obj.set_score(99)
print(obj.score)       # 99
obj2.set_score(100)  # AttributeError: 'Obj' object has no attribute 'set_score'
"""
由以上代码可知，Obj类有一个方法：eat()方法。通过[types.MethodType(方法名, 对象名)]给类对象obj动态添加了对象方法set_score()，同理，Obj的另一个类对象obj2不能调用这个方法
注：通过对象名添加的对象方法，只有这个对象能使用
"""
# 2.2 添加静态方法
Obj.static_func = static_func
Obj.static_func()
obj.static_func()
obj2.static_func()
"""
由以上代码可知，通过[类名.静态方法名]给类Obj动态添加了静态方法static_func()，Obj类的Obj的类对象obj和obj2都能调用这个方法
注：通过类名添加的静态方法，这个类及这个类的所有对象都能使用
"""
# 2.3 添加类方法
Obj.class_func = class_func
Obj.class_func()
obj.class_func()
obj2.class_func()
"""
由以上代码可知，通过[类名.类方法名]给类Obj动态添加了类方法class_func()，Obj类的Obj的类对象obj和obj2都能调用这个方法
注：通过类名添加的类方法，这个类及这个类的所有对象都能使用
"""

"""
3、__slots__的使用
通过以上内容，我们知道了如何动态的添加属性和方法。但是，如果我们想要限制class的属性该怎么办？
例如：只允许Obj实例添加name和age属性。为了达到这个目的，Python允许在定义class的时候，
定义一个特殊变量__slots__来限制该class能添加的属性。
"""


class Obj(object):
    __slots__ = ('name', 'age')


obj = Obj()
obj.name = 'zhangyafei'
obj.age = 23
obj.score = 99  # AttributeError: 'Obj' object has no attribute 'score'

Obj.score = 100
print(obj.score)        # 100
obj.score = 99  # AttributeError: 'Obj' object attribute 'score' is read-only
"""
通过以上代码可知，__slots__对Obj类的动态添加没有限制，而Obj类对象obj不能再动态添加对象属性和方法。

对于__slot__有以下几个需要注意的地方：

__slots__只对类对象进行限制，不对类进行限制
__slots__不仅限制类对象的属性，还限制类对象的方法
__slots__仅对当前类起作用，对继承的子类不起作用
在子类中定义__slots__，子类允许定义的属性就是自身的__slots__加上父类的__slots__
"""